package com.keithpower.freshdox;

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Hashtable;


/**
 * DocTest
 * 
 * This class is the workhorse of the DocTest project. It is reponsible for coordinating the 
 * extraction of tests from a document (InputStream), and the execution of those tests.
 * 
 * The actual test extraction is delegated to an instance of a class implementing the TestExtracter
 * interface.
 * 
 * The test executions are delegated to classes implementing the TestExecuter interface.
 * 
 * The format of an embedded test varies depending on the TestExtracter targetted, but check out
 * DefaultTestExtracter for the default XML tag based scheme.
 * 
 * 
 * @version 0.1
 * @author Keith Power
 */
public class DocTest
{
	private static final String CONFIG_FILE = "config.xml";
	
	/**
	 * File config read from last. Used for reloading config.
	 */
	private String lastConfigFile;
		
	/**
	 * TestExtracter to use to extract tests from document
	 */
	private TestExtracter testExtracter;

	/**
	 * Hashtable to store TestExecuters using language as the key 
	 */
	private Hashtable testExecuters;
	
	
	public DocTest()
	{
		loadConfiguration(CONFIG_FILE);
	}

	// ---------------------  CONFIGURATION METHODS --------------------------------------
	
	/**
	 * Read configuration from file and set values accordingly.
	 * @param configFile File to read configuration from  
	 */
	public void loadConfiguration(String configFile)
	{
		this.lastConfigFile = configFile;
		
		// TODO Read from configFile
		this.testExecuters = new Hashtable();
		
		setTestExtracter("com.keithpower.freshdox.DefaultTestExtracter");
		
		addTester("Java(BeanShell)", "com.keithpower.freshdox.JavaBSHTester");
        addTester("C", "com.keithpower.freshdox.BasicCExecuter");        
	}

	/**
	 * Read configuration from file and set values accordingly.
	 * @param configFile File to read configuration from  
	 */
	private void reloadConfiguration()
	{
		loadConfiguration(this.lastConfigFile);
	}

	
	public void addTester(String languageKey, String className)
	{

		try
        {
			// make instance of className
			Class executerClass = Class.forName(className);
            TestExecuter executer = (TestExecuter) executerClass.newInstance();
            
            // add to hashtable using language as key
            this.testExecuters.put(languageKey, executer);
        }catch (Exception ex)
        {
            System.err.println("Error instantiating TestExecuter: " + className);
            ex.printStackTrace();
        }
	}

	/**
	 * Set the TestExtracter to be used
	 * @param className Name of a class implementing TestExtracter interface
	 */
	public void setTestExtracter(String className)
	{
		// make instance of className, store in testExtracter
		try
        {
            Class extracterClass = Class.forName(className);
            this.testExtracter = (TestExtracter) extracterClass.newInstance();
        }catch (Exception ex)
        {
            System.err.println("Error instantiating TestExtracter: " + className);
            ex.printStackTrace();
        }
	}

	// ---------------------  ACTION METHODS --------------------------------------
	
	public String processDocument(InputStream docInpStream)
	{		
		Test [] tests = extractTests( docInpStream );
		String output = "";
		// Now process each test using relevant TestExecuter 
		for (int i = 0; i < tests.length; i++)
		{
			String language = tests[i].getLanguage();
			if(!this.testExecuters.containsKey(language))
			{
				// just acknowledge and move on to next test
				System.err.println("Don't have a TestExecuter to execute tests of type: "+language);
			}
			else
			{
				TestExecuter tester = (TestExecuter)this.testExecuters.get(language);
				boolean passed = tester.test(tests[i], tests);
				
				if(!passed)
				{
					//System.err.println("Test failed: "+tests[i].getName()+" "+tests[i].getError());
                    output += "Test failed: "+tests[i].getName()+" "+tests[i].getError()+"\n";
				}
				
				// TODO update statistics
			}
		}
		return output;
	}

	/**
	 * Call current TestExtracter to get tests
	 * @param stream
	 * @return Test[] array of Tests
	 */
	public Test[] extractTests(InputStream stream)
	{
		return this.testExtracter.extractTests( stream );
	}

	/**
	 * Get rid of any state. Keep configuration.
	 */
	public void reset()
	{
		// just reload configuration
        reloadConfiguration();
	}
	
	// ---------------------  HELPER METHODS --------------------------------------
	

	
	// ---------------------  MAIN METHOD --------------------------------------
	
	public static void main(String[] args) throws Throwable
	{	
		DocTest tester = new DocTest( );
        String output = tester.processDocument(new DataInputStream( new FileInputStream(args[0])));
        System.out.println(output);    
	}
}
